#include <windows.h>
#include <gl/gl.h>
#include "bmp.h"
#include <iostream>

typedef unsigned short UNSIGNED16; // a 16-bit value
typedef unsigned UNSIGNED32; // a 32-bit value
typedef int SIGNED32; // a 32-bit value

struct BmpFileHeader
{
  UNSIGNED32  bfSize;       // # of bytes for bitmap file
  UNSIGNED16  bfReserved1;  // should be zero
  UNSIGNED16  bfReserved2;  // should be zero
  UNSIGNED32  bfOffBits;    // offset info the file where the bits are located
};

struct BmpInfoHeader
{
  UNSIGNED32  biSize;           // size of this struct
  SIGNED32    biWidth;          // image width
  SIGNED32    biHeight;         // image height (can be negative -- negative means top-down orientation)
  UNSIGNED16  biPlanes;         // # bitplanes (must be 1)
  UNSIGNED16  biBitCount;       // bits per pixel
  UNSIGNED32  biCompression;    // BI_RGB, BI_RLE8, BI_RLE4, BI_BITFIELDS, BI_JPEG, BI_PNG
  UNSIGNED32  biSizeImage;      // size in bytes -- can be zero for BI_RGB (uncompressed) images
  SIGNED32    biXPelsPerMeter;  // _pels_?!?!? wot the bloody 'ell?!?
  SIGNED32    biYPelsPerMeter; 
  UNSIGNED32  biClrUsed;        // # color indices in color table actually used
  UNSIGNED32  biClrImportant;   // # colors in color table important for displaying image
};

bool ReadType( FILE *fp );
bool ReadFileHeader( FILE *fp, struct BmpFileHeader *pBmpFileHeader );
bool ReadInfoHeader( FILE *fp, struct BmpInfoHeader* pBmpinfoHeader );
bool ReadPixelData( FILE *fp, struct BmpInfoHeader* pBmpinfoHeader,unsigned char* &pixelData);

// header info
bool ReadFile(const char *filename, int *nImagewidth, int *nImageHeight, unsigned char* &m_pixelData)
{
  // a bmp file is laid out on disk like this:
  //   'BM'
  //   BmpFileHeader
  //   BmpInfoHeader
  //   colortable (possibly empty)
  //   pixel data

    BmpFileHeader pBmpFileHeader;
    BmpInfoHeader pBmpInfoHeader;
    FILE *fp;
    fopen_s(&fp, filename, "rb" );
    if( !fp ) {
        fprintf( stderr, "Couldn't open image file %s\n", filename );
        return false;
    }
  if( !ReadType( fp ) )
  {
    fprintf( stderr, "Bad type code for image file %s\n", filename );
    fclose( fp );
    return false;
  }
  
  if( !ReadFileHeader( fp,&pBmpFileHeader ) )
  {
    fprintf( stderr, "Invalid file header for image file %s\n", filename );
    fclose( fp );
    return false;
  }
  if( !ReadInfoHeader( fp,&pBmpInfoHeader ) )
  {
    fprintf( stderr, "Invalid info header for image file %s\n", filename );
    fclose( fp );
    return false;
  }
  m_pixelData = NULL;
  if( !ReadPixelData( fp,&pBmpInfoHeader,m_pixelData ) )
  {
    fprintf( stderr, "Couldn't read pixel data for image file %s\n", filename );
    fclose( fp );
    return false;
  }
    *nImagewidth = pBmpInfoHeader.biWidth;
    *nImageHeight = pBmpInfoHeader.biHeight;
    fclose(fp);
  return true;
}

bool ReadType( FILE *fp )
{
  char type[2];
  int numRead = (int)fread( type, 1, 2, fp );
  if( numRead != 2 )
    return false;
  
  // The unique header on a .bmp file that identifies it as such is
  // the string "BM". Pathetic, no?
  return type[0] == 'B' && type[1] == 'M';
}

bool ReadFileHeader( FILE *fp, struct BmpFileHeader *pBmpFileHeader )
{
    int numRead = (int)fread(pBmpFileHeader,sizeof(BmpFileHeader),1,fp );
    //  printf("Read %d\n",numRead);
     return (numRead==1);
 }

bool ReadInfoHeader( FILE *fp, struct BmpInfoHeader* pBmpinfoHeader )
{
  int numRead = (int)fread( pBmpinfoHeader, sizeof(BmpInfoHeader),1, fp );
//    printf("Read %d\n",numRead);
  if( numRead != 1 )
      return false;
  // check to see if it's in a format we understand
  if( pBmpinfoHeader->biCompression != 0 )
    return false;
  if( pBmpinfoHeader->biBitCount != 24 && pBmpinfoHeader->biBitCount != 32 )
    return false;
  return true;
}

bool ReadPixelData( FILE *fp, struct BmpInfoHeader* pBmpinfoHeader,unsigned char* &m_pixelData)
{
  if( m_pixelData )
    delete[] m_pixelData;

  int bytesPerPixel = pBmpinfoHeader->biBitCount/8;
  int oldwidth = pBmpinfoHeader->biWidth;
  int newwidth = (oldwidth*3+3)/4*4; 
//  printf("Newwidth %d\n",newwidth);
  int newheight = pBmpinfoHeader->biHeight;
  int size = newwidth*newheight;
  m_pixelData = new unsigned char[ size ];
  //pixelData = new unsigned char[ size ];

  int numRead = (int)fread( m_pixelData, 1, size, fp );
//  printf("Read %d\n",numRead);
  if( numRead != size ){
    delete[] m_pixelData;
    m_pixelData = NULL;
    return false;
  }
  return true;
}

BMP ReadBmpImage( const char *filename,  unsigned char r, unsigned char g, unsigned char b, bool alpha/* = false*/ )
{
    BMP bmp;

    int nImagewidth;
    int nMemorywidth;
    int nImageheight;
    unsigned char *m_pixelData = NULL,*pixelData = NULL;
    if (ReadFile(filename,&nImagewidth,&nImageheight,m_pixelData))
    {
        nMemorywidth = (nImagewidth*3+3)/4*4; 

        pixelData = new unsigned char[4*nImagewidth*nImageheight];
    
        int pcnt=0;
        for(int i=0;i< nImageheight;i++)
            for(int j=0;j< nImagewidth*3; j = j+3)
            { 
                pixelData[4*pcnt + 0]=m_pixelData[i*nMemorywidth + j + 2];
                pixelData[4*pcnt + 1]=m_pixelData[i*nMemorywidth + j + 1];       
                pixelData[4*pcnt + 2]=m_pixelData[i*nMemorywidth + j];

                if (alpha && pixelData[4*pcnt]==r && pixelData[4*pcnt + 1]==g && pixelData[4*pcnt + 2]==b)
                    pixelData[4*pcnt + 3]=0;
                else pixelData[4*pcnt+3]=255;

                pcnt++;
            }

        bmp.width=nImagewidth;
        bmp.height=nImageheight;
        bmp.data=pixelData;
    }

    // cleanup memory
    if (m_pixelData)
        delete[] m_pixelData;

    return bmp;
}